/*
 * commons is a based project implemented
 * Copyright (C) 2024 Jasmine
 */
package com.commons.redis.config;

import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.redisson.codec.JsonJacksonCodec;
import org.redisson.config.ClusterServersConfig;
import org.redisson.config.SentinelServersConfig;
import org.redisson.config.SingleServerConfig;
import org.redisson.spring.starter.RedissonAutoConfigurationCustomizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.util.CollectionUtils;

import com.commons.core.constant.StringPool;
import com.commons.core.utils.BeanCopyUtil;
import com.commons.redis.props.RedissonProperties;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * Redisson 自动配置
 */
@AutoConfiguration
@ConditionalOnProperty(prefix = "spring.data.redisson", name = "enabled", havingValue = "true")
@EnableConfigurationProperties(RedissonProperties.class)
public class RedissonAutoConfiguration {

    private static final Logger log = LoggerFactory.getLogger(RedissonAutoConfiguration.class);

    private final RedissonProperties properties;
    private final RedisProperties redisProperties;
    private final ObjectMapper objectMapper;

    public RedissonAutoConfiguration(RedissonProperties properties, RedisProperties redisProperties,
        ObjectMapper objectMapper) {
        this.properties = properties;
        this.redisProperties = redisProperties;
        this.objectMapper = objectMapper;
    }

    @Bean
    public RedissonAutoConfigurationCustomizer redissonAutoConfigurationCustomizer() {
        return config -> {
            RedissonProperties.Mode mode = properties.getMode();
            String protocol = redisProperties.getSsl().isEnabled() ? "rediss://" : "redis://";
            switch (mode) {
                case CLUSTER -> {
                    ClusterServersConfig clusterServersConfig = config.useClusterServers();
                    ClusterServersConfig customClusterServersConfig = properties.getClusterServersConfig();
                    if (null != customClusterServersConfig) {
                        BeanCopyUtil.copyProperty(customClusterServersConfig, clusterServersConfig);
                        clusterServersConfig.setNodeAddresses(customClusterServersConfig.getNodeAddresses());
                    }
                    // 下方配置如果为空，则使用 Redis 的配置
                    if (CollectionUtils.isEmpty(clusterServersConfig.getNodeAddresses())) {
                        List<String> nodeList = redisProperties.getCluster().getNodes();
                        nodeList.stream().map(node -> protocol + node).forEach(clusterServersConfig::addNodeAddress);
                    }
                    if (StringUtils.isBlank(clusterServersConfig.getPassword())) {
                        clusterServersConfig.setPassword(redisProperties.getPassword());
                    }
                }
                case SENTINEL -> {
                    SentinelServersConfig sentinelServersConfig = config.useSentinelServers();
                    SentinelServersConfig customSentinelServersConfig = properties.getSentinelServersConfig();
                    if (null != customSentinelServersConfig) {
                        BeanCopyUtil.copyProperty(customSentinelServersConfig, sentinelServersConfig);

                        sentinelServersConfig.setSentinelAddresses(customSentinelServersConfig.getSentinelAddresses());
                    }
                    // 下方配置如果为空，则使用 Redis 的配置
                    if (CollectionUtils.isEmpty(sentinelServersConfig.getSentinelAddresses())) {
                        List<String> nodeList = redisProperties.getSentinel().getNodes();
                        nodeList.stream().map(node -> protocol + node)
                            .forEach(sentinelServersConfig::addSentinelAddress);
                    }
                    if (StringUtils.isBlank(sentinelServersConfig.getPassword())) {
                        sentinelServersConfig.setPassword(redisProperties.getPassword());
                    }
                    if (StringUtils.isBlank(sentinelServersConfig.getMasterName())) {
                        sentinelServersConfig.setMasterName(redisProperties.getSentinel().getMaster());
                    }
                }
                default -> {
                    SingleServerConfig singleServerConfig = config.useSingleServer();
                    SingleServerConfig customSingleServerConfig = properties.getSingleServerConfig();
                    if (null != customSingleServerConfig) {
                        BeanCopyUtil.copyProperty(properties.getSingleServerConfig(), singleServerConfig);
                    }
                    // 下方配置如果为空，则使用 Redis 的配置
                    singleServerConfig.setDatabase(redisProperties.getDatabase());
                    if (StringUtils.isBlank(singleServerConfig.getPassword())) {
                        singleServerConfig.setPassword(redisProperties.getPassword());
                    }
                    if (StringUtils.isBlank(singleServerConfig.getAddress())) {
                        singleServerConfig.setAddress(
                            protocol + redisProperties.getHost() + StringPool.COLON + redisProperties.getPort());
                    }
                }
            }
            // Jackson 处理
            config.setCodec(new JsonJacksonCodec(objectMapper));
            log.debug("[Commons Starter] - Auto Configuration 'Redisson' completed initialization.");
        };
    }
}
